import { GameEvent, SysOrder } from "../../GameDef";
import { Vec2 } from "../../lib/kgame/d2/math/Vec2";
import CmpTrans from "../../lib/kgame/ecs/CmpTrans";
import { IECSFilter } from "../../lib/kgame/ecs/ECSFilter";
import { ECSSys } from "../../lib/kgame/ecs/ECSSys";
import { CmpFood } from "../cmp/CmpFood";
import { CmpSnake } from "../cmp/CmpSnake";
import { CmpSnakeBody } from "../cmp/CmpSnakeBody";
import { CmpSnakeHead } from "../cmp/CmpSnakeHead";
import { FoodFactory } from "../factory/FoodFactory";
import { SnakeFactory } from "../factory/SnakeFactory";
import { SCmpWorld } from "../scmp/SCmpWorld";

export default class SysSnakeHead extends ECSSys {

    world: SCmpWorld;

    get updateOrder(): number {
        return SysOrder.snakeHead;
    }

    get ifilter(): IECSFilter {
        return { includeOne: [CmpSnakeHead] };
    }

    protected onStart(): void {
        this.world = this.scene.getCmp(SCmpWorld);
    }

    onDestroy(): void {
        this.world = null;
    }

    protected onUpdate(): void {
        this.world.snakeQuadTree.clear();

        this.filter.entitySet.forEach(e => {
            const head = e.getCmp(CmpSnakeHead);
            head.trans.rot = head.move.moveDir;
            this.updateSprint(head);
            this.checkEat(head);
            head.updateAttr();
            this.recordHeadPath(head);
            this.moveBody(head);
            this.updateBodyLen(head);
            this.updateGhost(head);
        });

        this.filter.entitySet.forEach(e => {
            const head = e.getCmp(CmpSnakeHead);
            this.checkDead(head);
        });
    }

    /**
     * 更新冲刺状态
     * @param head
     */
    private updateSprint(head: CmpSnakeHead) {
        if (head.isSprint) {
            //冲刺消耗分数
            const cost = head.getSprintCost();
            if (head.score > cost) {
                head.score -= cost;
                head.sprintScore += cost;
                if (head.sprintScore >= cost / 10) {
                    //消耗的分数转化为尾部食物
                    const randDis = head.radius / 2;
                    const lifeTime = this.scene.rand.range(3, 6);
                    const color = this.scene.rand.choose(head.colors);
                    FoodFactory.createFood(this.scene, Vec2.TPos(
                        head.tail.pos.x + this.scene.rand.shake(randDis),
                        head.tail.pos.y + this.scene.rand.shake(randDis),
                    ), Math.min(5, head.sprintScore / 2), lifeTime, color);
                    head.sprintScore = 0;
                }
            } else {
                //分数不够停止
                head.isSprint = false;
            }
        }
    }

    /**
     * 检测吃食物
     * @param head
     */
    private checkEat(head: CmpSnakeHead) {
        head.foodCollider.update(head.pos);
        this.world.foodGrid.getCoverItems(head.foodCollider.worldAABB).forEach(food => {
            if (head.foodCollider.testCollide(food.collider)) {
                head.score += food.score;
                this.world.eatenFoods.push({
                    id: food.entityId,
                    eaterId: head.entityId
                });
                this.world.foodGrid.removeItem(food);
                food.removeEntity();
            }
        });
    }

    /**
     * 记录蛇头路径点
     * @param head
     */
    private recordHeadPath(head: CmpSnakeHead) {
        const { pos } = head.entity;
        const rot = head.trans.rot;

        let path = head.pathRecy;
        if (path) {
            head.pathRecy = path.next;
            path.rot = rot;
            Vec2.setPosTo(path, pos);
        } else {
            path = { x: pos.x, y: pos.y, rot };
        }
        path.next = head.path;
        head.path = path;

        head.snake.pos.setPosTo(pos);
        head.snake.trans.rot = rot;
    }

    /**
     * 根据蛇头路径移动蛇身
     * @param head
     */
    private moveBody(head: CmpSnakeHead) {
        let path = head.path.next;
        head.bodys.some((body, idx) => {
            if (idx > 0) {
                const last = head.bodys[idx - 1];
                while (path) {
                    const dis = last.pos.dis(path);
                    const space = dis - head.space;
                    if (space > 0) {
                        body.pos.setPosTo(path);
                        body.getCmp(CmpTrans).rot = path.rot;
                        body.pos.moveRad(path.rot, space);
                        break;
                    }
                    path = path.next;
                }
                return !path;
            }
        });

        if (path) {
            head.pathTail = path;
            const recy = path.next;
            if (recy) {
                //回收已不需要的路径
                head.pathTail.next = null;
                let tail = recy;
                while (tail.next) {
                    tail = tail.next;
                }
                tail.next = head.pathRecy;
                head.pathRecy = recy;
            }
        }

        //更新碰撞器
        head.bodys.forEach(body => {
            body.collider.update(body.pos);
            this.world.snakeQuadTree.addItem(body, body.collider.worldAABB);
        });
    }

    /**
     * 更新蛇身长度
     * @param head
     */
    private updateBodyLen(head: CmpSnakeHead) {
        let grow = head.len - head.bodys.length;
        while (grow > 0) {
            //变长
            --grow;
            SnakeFactory.createBody(head);
        }
        while (grow < 0) {
            //缩短
            ++grow;
            const body = head.bodys.pop();
            this.world.snakeQuadTree.removeItem(body);
            body.removeEntity();
        }
    }

    /**
     * 更新幽灵状态
     * @param head
     */
    private updateGhost(head: CmpSnakeHead) {
        if (head.ghostTime > 0) {
            head.ghostTime -= this.scene.dt;
            if (head.ghostTime <= 0) {
                head.ghostTime = 0;
                this.scene.send(GameEvent.snakeGhost, head.snake);
            }
        }
    }

    /**
     * 检测碰撞死亡
     * @param head
     */
    private checkDead(head: CmpSnakeHead) {
        if (head.snake.doRemove) {
            this.dead(head, null);
            return;
        }


        head.snakeCollider.radius = head.radius;
        head.snakeCollider.update(head.pos);

        let killer: CmpSnakeBody;
        if (head.ghostTime <= 0) {
            const bodys = this.world.snakeQuadTree.getCoverItems(head.snakeCollider.worldAABB);
            for (const body of bodys) {
                if (body.head === head) {
                    //自己的
                    continue;
                }
                if (body.head.ghostTime > 0) {
                    //幽灵状态忽略检测
                    continue;
                }

                if (head.snakeCollider.testCollide(body.collider)) {
                    //撞到了
                    killer = body;
                    break;
                }
            }
        }

        if (killer) {
            this.dead(head, killer.head.snake);
        } else {
            const world = this.scene.getCmp(SCmpWorld);
            if (world.isOutEdge(head.snakeCollider.worldAABB)) {
                this.dead(head, "edge");
            }
        }
    }

    /**
     * 死亡
     * @param head
     * @param killer
     */
    private dead(head: CmpSnakeHead, killer: CmpSnake | string) {
        const { snake } = head;
        snake.killer = killer;
        snake.respawnCd = 2;
        const oneScore = 5 + head.score / 3 / head.len;
        head.bodys.forEach(body => {
            const score = oneScore * this.scene.rand.range(0.9, 1.1);
            const randDis = CmpFood.getSize(score) / 2;
            const lifeTime = this.scene.rand.range(10, 30);
            const color = head.colors[body.idx % head.colors.length];
            FoodFactory.createFood(this.scene, Vec2.TPos(
                body.pos.x + this.scene.rand.shake(randDis),
                body.pos.y + this.scene.rand.shake(randDis)
            ), score, lifeTime, color);
            this.world.snakeQuadTree.removeItem(body);
            body.entity.removeSelf();
        });
        snake.head = null;
        if (killer) {
            this.scene.send(GameEvent.snakeDead, snake);
        }
    }

}